//===- ArithPatterns.td - Arith dialect patterns -*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef ARITH_PATTERNS
#define ARITH_PATTERNS

include "mlir/IR/PatternBase.td"
include "mlir/Dialect/Arith/IR/ArithOps.td"

// Create zero attribute of type matching the argument's type.
def GetZeroAttr : NativeCodeCall<"$_builder.getZeroAttr($0.getType())">;

// Add two integer attributes and create a new one with the result.
def AddIntAttrs : NativeCodeCall<"addIntegerAttrs($_builder, $0, $1, $2)">;

// Subtract two integer attributes and createa a new one with the result.
def SubIntAttrs : NativeCodeCall<"subIntegerAttrs($_builder, $0, $1, $2)">;

//===----------------------------------------------------------------------===//
// AddIOp
//===----------------------------------------------------------------------===//

// addi is commutative and will be canonicalized to have its constants appear
// as the second operand.

// addi(addi(x, c0), c1) -> addi(x, c0 + c1)
def AddIAddConstant :
    Pat<(Arith_AddIOp:$res
          (Arith_AddIOp $x, (ConstantLikeMatcher APIntAttr:$c0)),
          (ConstantLikeMatcher APIntAttr:$c1)),
        (Arith_AddIOp $x, (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1)))>;

// addi(subi(x, c0), c1) -> addi(x, c1 - c0)
def AddISubConstantRHS :
    Pat<(Arith_AddIOp:$res
          (Arith_SubIOp $x, (ConstantLikeMatcher APIntAttr:$c0)),
          (ConstantLikeMatcher APIntAttr:$c1)),
        (Arith_AddIOp $x, (Arith_ConstantOp (SubIntAttrs $res, $c1, $c0)))>;

// addi(subi(c0, x), c1) -> subi(c0 + c1, x)
def AddISubConstantLHS :
    Pat<(Arith_AddIOp:$res
          (Arith_SubIOp (ConstantLikeMatcher APIntAttr:$c0), $x),
          (ConstantLikeMatcher APIntAttr:$c1)),
        (Arith_SubIOp (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1)), $x)>;

def IsScalarOrSplatNegativeOne :
    Constraint<And<[
      CPred<"succeeded(getIntOrSplatIntValue($0))">,
      CPred<"getIntOrSplatIntValue($0)->isAllOnes()">]>>;

// addi(x, muli(y, -1)) -> subi(x, y)
def AddIMulNegativeOneRhs :
    Pat<(Arith_AddIOp
           $x,
           (Arith_MulIOp $y, (ConstantLikeMatcher AnyAttr:$c0))),
        (Arith_SubIOp $x, $y),
        [(IsScalarOrSplatNegativeOne $c0)]>;

// addi(muli(x, -1), y) -> subi(y, x)
def AddIMulNegativeOneLhs :
    Pat<(Arith_AddIOp
           (Arith_MulIOp $x, (ConstantLikeMatcher AnyAttr:$c0)),
           $y),
        (Arith_SubIOp $y, $x),
        [(IsScalarOrSplatNegativeOne $c0)]>;

//===----------------------------------------------------------------------===//
// AddUIExtendedOp
//===----------------------------------------------------------------------===//

// addui_extended(x, y) -> [addi(x, y), x], when the `overflow` result has no
// uses. Since the 'overflow' result is unused, any replacement value will do.
def AddUIExtendedToAddI:
    Pattern<(Arith_AddUIExtendedOp:$res $x, $y),
             [(Arith_AddIOp $x, $y), (replaceWithValue $x)],
             [(Constraint<CPred<"$0.getUses().empty()">> $res__1)]>;

//===----------------------------------------------------------------------===//
// SubIOp
//===----------------------------------------------------------------------===//

// subi(addi(x, c0), c1) -> addi(x, c0 - c1)
def SubIRHSAddConstant :
    Pat<(Arith_SubIOp:$res
          (Arith_AddIOp $x, (ConstantLikeMatcher APIntAttr:$c0)),
          (ConstantLikeMatcher APIntAttr:$c1)),
        (Arith_AddIOp $x, (Arith_ConstantOp (SubIntAttrs $res, $c0, $c1)))>;

// subi(c1, addi(x, c0)) -> subi(c1 - c0, x)
def SubILHSAddConstant :
    Pat<(Arith_SubIOp:$res
          (ConstantLikeMatcher APIntAttr:$c1),
          (Arith_AddIOp $x, (ConstantLikeMatcher APIntAttr:$c0))),
        (Arith_SubIOp (Arith_ConstantOp (SubIntAttrs $res, $c1, $c0)), $x)>;

// subi(subi(x, c0), c1) -> subi(x, c0 + c1)
def SubIRHSSubConstantRHS :
    Pat<(Arith_SubIOp:$res
          (Arith_SubIOp $x, (ConstantLikeMatcher APIntAttr:$c0)),
          (ConstantLikeMatcher APIntAttr:$c1)),
        (Arith_SubIOp $x, (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1)))>;

// subi(subi(c0, x), c1) -> subi(c0 - c1, x)
def SubIRHSSubConstantLHS :
    Pat<(Arith_SubIOp:$res
          (Arith_SubIOp (ConstantLikeMatcher APIntAttr:$c0), $x),
          (ConstantLikeMatcher APIntAttr:$c1)),
        (Arith_SubIOp (Arith_ConstantOp (SubIntAttrs $res, $c0, $c1)), $x)>;

// subi(c1, subi(x, c0)) -> subi(c0 + c1, x)
def SubILHSSubConstantRHS :
    Pat<(Arith_SubIOp:$res
          (ConstantLikeMatcher APIntAttr:$c1),
          (Arith_SubIOp $x, (ConstantLikeMatcher APIntAttr:$c0))),
        (Arith_SubIOp (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1)), $x)>;

// subi(c1, subi(c0, x)) -> addi(x, c1 - c0)
def SubILHSSubConstantLHS :
    Pat<(Arith_SubIOp:$res
          (ConstantLikeMatcher APIntAttr:$c1),
          (Arith_SubIOp (ConstantLikeMatcher APIntAttr:$c0), $x)),
        (Arith_AddIOp $x, (Arith_ConstantOp (SubIntAttrs $res, $c1, $c0)))>;

// subi(subi(a, b), a) -> subi(0, b)
def SubISubILHSRHSLHS :
    Pat<(Arith_SubIOp:$res (Arith_SubIOp $x, $y), $x),
        (Arith_SubIOp (Arith_ConstantOp (GetZeroAttr $y)), $y)>;

//===----------------------------------------------------------------------===//
// MulSIExtendedOp
//===----------------------------------------------------------------------===//

// mulsi_extended(x, y) -> [muli(x, y), x], when the `high` result is unused.
// Since the `high` result it not used, any replacement value will do.
def MulSIExtendedToMulI :
    Pattern<(Arith_MulSIExtendedOp:$res $x, $y),
        [(Arith_MulIOp $x, $y), (replaceWithValue $x)],
        [(Constraint<CPred<"$0.getUses().empty()">> $res__1)]>;


def IsScalarOrSplatOne :
    Constraint<And<[
      CPred<"succeeded(getIntOrSplatIntValue($0))">,
      CPred<"*getIntOrSplatIntValue($0) == 1">]>>;

// mulsi_extended(x, 1) -> [x, extsi(cmpi slt, x, 0)]
def MulSIExtendedRHSOne :
    Pattern<(Arith_MulSIExtendedOp $x, (ConstantLikeMatcher AnyAttr:$c1)),
            [(replaceWithValue $x),
             (Arith_ExtSIOp(Arith_CmpIOp
                              (NativeCodeCall<"arith::CmpIPredicate::slt">),
                              $x,
                              (Arith_ConstantOp (GetZeroAttr $x))))],
            [(IsScalarOrSplatOne $c1)]>;

//===----------------------------------------------------------------------===//
// MulUIExtendedOp
//===----------------------------------------------------------------------===//

// mului_extended(x, y) -> [muli(x, y), x], when the `high` result is unused.
// Since the `high` result it not used, any replacement value will do.
def MulUIExtendedToMulI :
    Pattern<(Arith_MulUIExtendedOp:$res $x, $y),
        [(Arith_MulIOp $x, $y), (replaceWithValue $x)],
        [(Constraint<CPred<"$0.getUses().empty()">> $res__1)]>;

//===----------------------------------------------------------------------===//
// XOrIOp
//===----------------------------------------------------------------------===//

// xori is commutative and will be canonicalized to have its constants appear
// as the second operand.

// not(cmpi(pred, a, b)) -> cmpi(~pred, a, b), where not(x) is xori(x, 1)
def InvertPredicate : NativeCodeCall<"invertPredicate($0)">;
def XOrINotCmpI :
    Pat<(Arith_XOrIOp
          (Arith_CmpIOp $pred, $a, $b),
          (ConstantLikeMatcher ConstantAttr<I1Attr, "1">)),
        (Arith_CmpIOp (InvertPredicate $pred), $a, $b)>;

// xor extui(x), extui(y) -> extui(xor(x,y))
def XOrIOfExtUI :
    Pat<(Arith_XOrIOp (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)), (Arith_ExtUIOp (Arith_XOrIOp $x, $y)),
      [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

// xor extsi(x), extsi(y) -> extsi(xor(x,y))
def XOrIOfExtSI :
    Pat<(Arith_XOrIOp (Arith_ExtSIOp $x), (Arith_ExtSIOp $y)), (Arith_ExtSIOp (Arith_XOrIOp $x, $y)),
      [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

//===----------------------------------------------------------------------===//
// CmpIOp
//===----------------------------------------------------------------------===//

// cmpi(== or !=, a ext iNN, b ext iNN) == cmpi(== or !=, a, b)
def CmpIExtSI :
    Pat<(Arith_CmpIOp $pred,
          (Arith_ExtSIOp $a),
          (Arith_ExtSIOp $b)),
        (Arith_CmpIOp $pred, $a, $b),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $a, $b),
         (Constraint<
            CPred<"$0.getValue() == arith::CmpIPredicate::eq || "
                  "$0.getValue() == arith::CmpIPredicate::ne">> $pred)]>;

// cmpi(== or !=, a ext iNN, b ext iNN) == cmpi(== or !=, a, b)
def CmpIExtUI :
    Pat<(Arith_CmpIOp $pred,
          (Arith_ExtUIOp $a),
          (Arith_ExtUIOp $b)),
        (Arith_CmpIOp $pred, $a, $b),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $a, $b),
         (Constraint<
            CPred<"$0.getValue() == arith::CmpIPredicate::eq || "
                  "$0.getValue() == arith::CmpIPredicate::ne">> $pred)]>;

//===----------------------------------------------------------------------===//
// IndexCastOp
//===----------------------------------------------------------------------===//

// index_cast(index_cast(x)) -> x, if dstType == srcType.
def IndexCastOfIndexCast :
    Pat<(Arith_IndexCastOp:$res (Arith_IndexCastOp $x)),
        (replaceWithValue $x),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $res, $x)]>;

// index_cast(extsi(x)) -> index_cast(x)
def IndexCastOfExtSI :
    Pat<(Arith_IndexCastOp (Arith_ExtSIOp $x)), (Arith_IndexCastOp $x)>;

//===----------------------------------------------------------------------===//
// IndexCastUIOp
//===----------------------------------------------------------------------===//

// index_castui(index_castui(x)) -> x, if dstType == srcType.
def IndexCastUIOfIndexCastUI :
    Pat<(Arith_IndexCastUIOp:$res (Arith_IndexCastUIOp $x)),
        (replaceWithValue $x),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $res, $x)]>;

// index_castui(extui(x)) -> index_castui(x)
def IndexCastUIOfExtUI :
    Pat<(Arith_IndexCastUIOp (Arith_ExtUIOp $x)), (Arith_IndexCastUIOp $x)>;


//===----------------------------------------------------------------------===//
// BitcastOp
//===----------------------------------------------------------------------===//

// bitcast(bitcast(x)) -> x
def BitcastOfBitcast :
    Pat<(Arith_BitcastOp (Arith_BitcastOp $x)), (replaceWithValue $x)>;

//===----------------------------------------------------------------------===//
// ExtSIOp
//===----------------------------------------------------------------------===//

// extsi(extui(x iN : iM) : iL) -> extui(x : iL)
def ExtSIOfExtUI :
    Pat<(Arith_ExtSIOp (Arith_ExtUIOp $x)), (Arith_ExtUIOp $x)>;

//===----------------------------------------------------------------------===//
// AndIOp
//===----------------------------------------------------------------------===//

// and extui(x), extui(y) -> extui(and(x,y))
def AndOfExtUI :
    Pat<(Arith_AndIOp (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)),
        (Arith_ExtUIOp (Arith_AndIOp $x, $y)),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

// and extsi(x), extsi(y) -> extsi(and(x,y))
def AndOfExtSI :
    Pat<(Arith_AndIOp (Arith_ExtSIOp $x), (Arith_ExtSIOp $y)),
        (Arith_ExtSIOp (Arith_AndIOp $x, $y)),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

//===----------------------------------------------------------------------===//
// OrIOp
//===----------------------------------------------------------------------===//

// or extui(x), extui(y) -> extui(or(x,y))
def OrOfExtUI :
    Pat<(Arith_OrIOp (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)),
        (Arith_ExtUIOp (Arith_OrIOp $x, $y)),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

// or extsi(x), extsi(y) -> extsi(or(x,y))
def OrOfExtSI :
    Pat<(Arith_OrIOp (Arith_ExtSIOp $x), (Arith_ExtSIOp $y)),
        (Arith_ExtSIOp (Arith_OrIOp $x, $y)),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

//===----------------------------------------------------------------------===//
// TruncIOp
//===----------------------------------------------------------------------===//

def ValuesWithSameType :
    Constraint<
      CPred<"llvm::all_equal({$0.getType(), $1.getType(), $2.getType()})">>;

def ValueWiderThan :
    Constraint<And<[
      CPred<"getScalarOrElementWidth($0) > getScalarOrElementWidth($1)">,
      CPred<"getScalarOrElementWidth($1) > 0">]>>;

def TruncationMatchesShiftAmount :
    Constraint<And<[
      CPred<"succeeded(getIntOrSplatIntValue($2))">,
      CPred<"(getScalarOrElementWidth($0) - getScalarOrElementWidth($1)) == "
              "*getIntOrSplatIntValue($2)">]>>;

// trunci(shrsi(x, c)) -> trunci(shrui(x, c))
def TruncIShrSIToTrunciShrUI :
    Pat<(Arith_TruncIOp:$tr
          (Arith_ShRSIOp $x, (ConstantLikeMatcher AnyAttr:$c0))),
        (Arith_TruncIOp (Arith_ShRUIOp $x, (Arith_ConstantOp $c0))),
        [(TruncationMatchesShiftAmount $x, $tr, $c0)]>;

// trunci(shrui(mul(sext(x), sext(y)), c)) -> mulsi_extended(x, y)
def TruncIShrUIMulIToMulSIExtended :
    Pat<(Arith_TruncIOp:$tr (Arith_ShRUIOp
                              (Arith_MulIOp:$mul
                                (Arith_ExtSIOp $x), (Arith_ExtSIOp $y)),
                              (ConstantLikeMatcher AnyAttr:$c0))),
        (Arith_MulSIExtendedOp:$res__1 $x, $y),
      [(ValuesWithSameType $tr, $x, $y),
       (ValueWiderThan $mul, $x),
       (TruncationMatchesShiftAmount $mul, $x, $c0)]>;

// trunci(shrui(mul(zext(x), zext(y)), c)) -> mului_extended(x, y)
def TruncIShrUIMulIToMulUIExtended :
    Pat<(Arith_TruncIOp:$tr (Arith_ShRUIOp
                              (Arith_MulIOp:$mul
                                (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)),
                              (ConstantLikeMatcher AnyAttr:$c0))),
        (Arith_MulUIExtendedOp:$res__1 $x, $y),
      [(ValuesWithSameType $tr, $x, $y),
       (ValueWiderThan $mul, $x),
       (TruncationMatchesShiftAmount $mul, $x, $c0)]>;

//===----------------------------------------------------------------------===//
// MulFOp
//===----------------------------------------------------------------------===//

// mulf(negf(x), negf(y)) -> mulf(x,y)
// (retain fastmath flags of original mulf)
def MulFOfNegF :
    Pat<(Arith_MulFOp (Arith_NegFOp $x, $_), (Arith_NegFOp $y, $_), $fmf),
        (Arith_MulFOp $x, $y, $fmf),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

//===----------------------------------------------------------------------===//
// DivFOp
//===----------------------------------------------------------------------===//

// divf(negf(x), negf(y)) -> divf(x,y)
// (retain fastmath flags of original divf)
def DivFOfNegF :
    Pat<(Arith_DivFOp (Arith_NegFOp $x, $_), (Arith_NegFOp $y, $_), $fmf),
        (Arith_DivFOp $x, $y, $fmf),
        [(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;

#endif // ARITH_PATTERNS
